$LINE_SEPARATOR = "\n"


class LexicalAnalyzer

  attr_reader :tokens,:buffer

  def initialize(file_path,errors)

    @errors       = errors
    @file_path    = file_path
    @line_counter = 0
    @char_counter = 0
    @file         = File.new file_path,"r" 
    @buffer       = [] 
    @tokens       = []

    @file.each_line { |line| @buffer << line }

  rescue Errno::ENOENT #arquivo nao encontrado"
    UI.log "Arquivo nao encontrado ou sem permissao."
  end

  def run
    count_line(@buffer.join)
    count_char(@buffer.join)
    tokens = []
    linha_atual = 0
    @buffer.join.split(/\n/).each do |line|
      linha_atual += 1
      line.split(/\s|\t/).each { |t| set_token t,linha_atual }
    end

    UI.log "No de linhas: "+@line_counter.to_s
    UI.log "No de caracteres: "+@char_counter.to_s
  end

  def dump
    @tokens.each { |token| UI.log token.to_s }
  end

  private

  def set_token(string,linha_atual)

    return if string.nil? or string.empty?

    rules = { 
              :reserved  => /^int|^begin|^end|^proc|^read|^write|^to|^do|^until|^while|^if|^then|^else|^func|^fi/,
              :id        => /^[a-zA-Z]{1}[a-zA-Z0-9]*/,
              :operator  => /^:=|^\+|^\<\>|^\<|^\>|^\>=|^\<=|^-|^\*|^\//,
              :delimiter => /^;|^,|^:|^\(|^\)/,
              :intnum    => /^[0-9]+/
            }

    rules_order = [:reserved,:id,:operator,:delimiter,:intnum]

    token_error = ""
    error = false
    rules_order.each do |rule|
      tokens = string.scan rules[rule]
      token = tokens[0]

      if token
        error = false
        found = true
      else
        error = true
        found = false
        next
      end

      line      = @buffer[linha_atual-1].gsub "\n",""
      character = line.index(string)
      @tokens << Token.new(token,rule,TokenPosition.new(linha_atual,character+1))
      ntoken = string[token.size,string.size]
      set_token ntoken,linha_atual

      break if found 
    end

    if error
      line_error      = @buffer[linha_atual-1].gsub "\n",""
      character_error = line_error.index(string)
      position = TokenPosition.new(linha_atual,character_error)
      token_error     = string[0,1]
      spaces          = ""

      character_error.times { |s| spaces += " "}

      @errors << "Erro Lexico: token #{token_error} nao identificado na #{position.to_s}"
      @errors << "\t#{line_error}"
      @errors << "\t#{spaces}|-----\n"

      ntoken = string[1,string.size]
      set_token ntoken,linha_atual
    end

  end

  def count_line(string)
    @line_counter = string.split($LINE_SEPARATOR).length
  end

  def count_char(string)
    @char_counter = string.length
  end

end
